package com.moli.iov.gateway.server;

import com.moli.iov.gateway.server.model.UpStreamData;
import com.moli.iov.gateway.server.task.UpStreamDataTask;
import com.moli.iov.gateway.util.GatewayUtil;
import com.moli.iov.util.ByteArrayUtil;
import com.moli.iov.util.SpringContextUtils;
import com.moli.iov.util.StringUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;

/**
 * Server 处理器 处理接收的消息(已拆包)
 *
 * @author <a href="mailto:liws@xingyuanauto.com">liws</a>
 * @version $Revision$
 * @since 2017年1月6日
 */
public class NettyServerHandler extends SimpleChannelInboundHandler<Object> {

    private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);

    VinThreadPoolExecutor upThreadPool = SpringContextUtils.getBean("upThreadPool");

    /**
     * 异常信息
     */
    private static final String CAUSE_MSG = "Connection reset by peer";

    NettyServerHandler() {
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg) {
        try {
            byte[] bytes = (byte[]) msg;
            logger.info("[上行 接收报文][" + ByteArrayUtil.toHexString(bytes) + "]");
            UpStreamData upStreamData = new UpStreamData(ctx.channel(), new Date(), bytes);
            String vin = GatewayUtil.getVin(bytes);
            if (StringUtil.isNotBlank(vin)) {
                upThreadPool.execute(vin, new UpStreamDataTask(upStreamData));
            } else {
                logger.error("解析vin出错，byte={}", ByteArrayUtil.toHexString(bytes));
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }

    /**
     * @see io.netty.channel.ChannelInboundHandlerAdapter#channelActive(io.netty.channel.ChannelHandlerContext)
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        try {
            super.channelActive(ctx);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }


    /**
     * 断开连接
     * @param ctx
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        try {
            String vin = NettyChannelManager.getInstance().getVinByChannel(ctx.channel().id().asLongText());
            logger.info("[客户端断开channelInactive][VIN=" + vin + "][channelId=" + ctx.channel().id().asLongText() + "][" + ctx.channel().remoteAddress() + "]");
            NettyChannelManager.getInstance().removeChannel(ctx.channel(), vin);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }


    /**
     * 捕获客户端异常断开
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        String message = cause.getMessage();
        String vin = NettyChannelManager.getInstance().getVinByChannel(ctx.channel().id().asLongText());
        try {
            //不处理tcp  rst信号
            if (!message.contains(CAUSE_MSG)) {
                logger.error("[客户端断开exceptionCaught][VIN=" + vin + "][channelId=" + ctx.channel().id().asLongText() + "][" + ctx.channel().remoteAddress() +
                        "][Cause: " + cause.getMessage() + "]");
                ctx.channel().close();
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }

    /**
     * 超时处理
     * @param ctx
     * @param evt
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        try {
            if (IdleStateEvent.class.isAssignableFrom(evt.getClass())) {
                IdleStateEvent event = (IdleStateEvent) evt;
                switch (event.state()) {
                    case READER_IDLE:
                        eventTriggered(ctx);
                        break;
                    case WRITER_IDLE:
                        eventTriggered(ctx);
                    case ALL_IDLE:
                        eventTriggered(ctx);
                        break;
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }

    private void eventTriggered(ChannelHandlerContext ctx) {
        String vin = NettyChannelManager.getInstance().getVinByChannel(ctx.channel().id().asLongText());
        ctx.channel().close();
        logger.info("[客户端超时断开][VIN=" + vin + "][channelId=" + ctx.channel().id().asLongText() + "][" + ctx.channel().remoteAddress() + "]");
    }

}
